﻿using System;
using System.Text;

namespace Red.EngineData.Protocol
{
    /// <summary>
    /// Helper class for string parsing.
    /// </summary>
    public sealed class TextParser
    {
        string _stream;
        int _position;
        StringBuilder _stringBuilder;

        /// <summary>
        /// Constructs.
        /// </summary>
        /// <param name="stream"></param>
        public TextParser( string stream )
        {
            _stream = stream;
            _position = 0;
            _stringBuilder = new StringBuilder();
        }

        /// <summary>
        /// Parse keyword if possible.
        /// </summary>
        /// <param name="keyword"></param>
        /// <returns>True if parsed, false if not.</returns>
        public bool ParseKeyword( string keyword )
        {
            for ( int i = 0; i < keyword.Length; ++i )
                if ( keyword[i] != _stream[_position + i] )
                    return false;

            _position += keyword.Length;
            return true;
        }

        /// <summary>
        /// Parse identifier which starts with letter and follows with alphanumeric characters.
        /// </summary>
        /// <param name="identifier"></param>
        /// <returns>True if parsed, false if not.</returns>
        public bool ParseIdentifier( out string identifier )
        {
            _stringBuilder.Clear();

            if ( Char.IsLetter( _stream[_position] ) == false && _stream[_position] != '_' )
            {
                identifier = "";
                return false;
            }

            while ( IsEndOfStream() == false && (Char.IsLetterOrDigit( _stream[_position] ) || _stream[_position] == '_') )
            {
                _stringBuilder.Append( _stream[_position] );
                _position++;
            }

            identifier = _stringBuilder.ToString();

            return true;
        }

        /// <summary>
        /// Parse number.
        /// </summary>
        /// <param name="number"></param>
        /// <returns>True if parsed, false if not.</returns>
        public bool ParseNumber( out int number )
        {
            _stringBuilder.Clear();
            if ( Char.IsDigit( _stream[_position] ) == false )
            {
                number = 0;
                return false;
            }

            while ( IsEndOfStream() == false && Char.IsDigit( _stream[_position] ) )
            {
                _stringBuilder.Append( _stream[_position] );
                _position++;
            }

            number = int.Parse( _stringBuilder.ToString() );

            return true;
        }

        /// <summary>
        /// Parse string.
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public bool ParseString( out string str )
        {
            _stringBuilder.Clear();

            if ( _stream[_position] != '"' )
            {
                str = "";
                return false;
            }

            ++_position; // skip initial quote
            while ( IsEndOfStream() == false && _stream[_position] != '"' )
            {
                _stringBuilder.Append( _stream[_position] );
                _position++;
            }
            if ( !IsEndOfStream() ) ++_position; // skip ending quote

            str = _stringBuilder.ToString();

            return true;
        }

        /// <summary>
        /// Check if end of stream was reached.
        /// </summary>
        /// <returns></returns>
        public bool IsEndOfStream()
        {
            return _position == _stream.Length;
        }
    }
}
